#include "stdafx.h"
#include "Thread.h"
#include <common/TimeVal.h>
#ifndef WIN32
#include <sys/select.h>
#endif

namespace catan
{
	Thread::Thread()
	{
#ifdef WIN32
		mHandle = 0;
		mId = 0;
#else
		mThread = 0;
#endif
		mStarted = 0;
	}

	Thread::~Thread()
	{
#ifdef WIN32
		if (mHandle != 0)
		{
			CloseHandle(mHandle);
			mHandle = 0;
		}
#endif
	}

#ifdef WIN32
	DWORD WINAPI ThreadFunc(void* thread)
#else
	void* ThreadFunc(void* thread)
#endif
	{
		Thread* t = (Thread*)thread;
		t->mStarted = true;
		t->Run();
		t->mStarted = false;
		return 0;
	}

	bool Thread::Start()
	{
		if (mStarted)
		{
			return false;
		}

#ifdef WIN32
		mHandle = CreateThread(0, 0, ThreadFunc, this, 0, &mId);
		return mHandle != 0;
#else
		return pthread_create(&mThread, 0, ThreadFunc, this) == 0;
#endif
	}

	void Thread::Join()
	{
		if (!mStarted)
		{
			return;
		}
#ifdef WIN32
		WaitForSingleObject(mHandle, INFINITE);
#else
		void* status;
		pthread_join(mThread, &status);
#endif
	}

	Thread::ID Thread::GetThreadId() const
	{
#ifdef WIN32
		return mId;
#else
		return mThread;
#endif
	}

	Thread::ID Thread::GetCurrentThreadId()
	{
#ifdef WIN32
		return ::GetCurrentThreadId();
#else
		return pthread_self();
#endif
	}

	bool Thread::operator ==(const Thread& other) const
	{
#ifdef WIN32
		return mHandle == other.mHandle && mId == other.mId && mHandle != 0 && other.mHandle != 0;
#else
		return pthread_equal(mThread, other.mThread) != 0;
#endif
	}

	bool Thread::operator !=(const Thread& other) const
	{
		return !(*this == other);
	}

	void Thread::Sleep(const TimeVal& interval)
	{
#ifdef WIN32
		::Sleep(interval.tv_sec * 1000 + interval.tv_usec / 1000);
#else
		TimeVal tv = interval;
		select(0, NULL, NULL, NULL, &tv);
#endif
	}

	void Thread::Sleep(unsigned long milliseconds)
	{
#ifdef WIN32
		::Sleep(milliseconds);
#else
		TimeVal tv;
	    tv.tv_sec = milliseconds / 1000;
		tv.tv_usec = milliseconds % 1000 * 1000;
		select(0, NULL, NULL, NULL, &tv);
#endif
	}

	void Thread::SchedYield()
	{
#ifdef WIN32
		::Sleep(0);
#else
		sched_yield();
#endif
	}
}
